/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package hxg.portmgr.core.ip;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.annotation.*;

/**
 *
 * @author hxg
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {})
@XmlRootElement(name = "IpAddress")
public class IpAddress implements Serializable {

    @XmlAttribute
    String ipAddr;
    @XmlAttribute
    int netMask;

    public IpAddress() {
    }

    public IpAddress(String ip, String mask) {
        this.ipAddr = ip;
        this.netMask = Integer.bitCount((int) getIntForIP(mask));
    }

    public IpAddress(String ip, int mask) {
        this.ipAddr = ip;
        this.netMask = mask;
    }

    //<editor-fold defaultstate="collapsed" desc="Getter And Setter">
    public void setIpAddr(String ipAddr) {
        this.ipAddr = ipAddr;
    }
    
    public void setNetMask(int netMask) {
        this.netMask = netMask;
    }
    
    public void setNetMask(String netMask) {
        this.netMask = Integer.bitCount((int) getIntForIP(netMask));
    }
    //</editor-fold>

    @Override
    public String toString() {
        return ipAddr + "/" + netMask;
    }

    public String getIpAddr() {
        return ipAddr;
    }

    public String getNetMaskString() {
        return getStringForIP(netMask);
    }

    public int getNetMask() {
        return netMask;
    }
//
    public IpSubnet getIpSubnet() {
        return IpSubnet.getIpSubnet(this);
    }

    public IpSubnet getUpIpSubnet() {
        return IpSubnet.getIpSubnet(ipAddr,netMask-1);
    }

    public static long getIntForIP(String ip) {
        String[] ips = ip.split("[.]");
        long ipnum = Integer.parseInt(ips[0]) * 256 * 65536l + Integer.parseInt(ips[1]) * 65536l + Integer.parseInt(ips[2]) * 256l + Long.parseLong(ips[3]);
        return ipnum;
    }

    public static String getStringForIP(int ip) {
        int ip1 = ip >>> 24;
        int ip2 = ip >> 16 << 24 >>> 24;
        int ip3 = ip >> 8 << 24 >>> 24;
        int ip4 = ip << 24 >>> 24;
        return ip1 + "." + ip2 + "." + ip3 + "." + ip4;
    }

    public static String getIpSubnet(String ip, String ipmask) {
        String ipsub = getStringForIP((int) (getIntForIP(ip) & getIntForIP(ipmask)));
        int intm = Integer.bitCount((int) getIntForIP(ipmask));
        return ipsub + "/" + intm;
    }

    public static String[] getIpListInIpSubnet(String ip_subnet) {
        if (ip_subnet.contains("/")) {
            String[] info = ip_subnet.split("/");
            if (info[1].contains(".")) {
                return getIpListInIpSubnet(info[0], info[1]);
            } else {
                return getIpListInIpSubnet(info[0], Integer.parseInt(info[1]));
            }
        } else {
            return new String[]{ip_subnet};
        }
    }

    public static String[] getIpListInIpSubnet(String ip, String ipmask) {
        int intm = Integer.bitCount((int) getIntForIP(ipmask));
        long count = (65536l * 65526l >> intm) + 1;
        String[] ips = new String[(int) count];
        int ipsubint = (int) (getIntForIP(ip) & getIntForIP(ipmask));
        for (int i = 0; i < count; i++) {
            ips[i] = getStringForIP(ipsubint++);
        }
        return ips;
    }

    public static String[] getIpListInIpSubnet(String ip, int mask) {
        long count = (65536l * 65526l >> mask) + 1;
        int ipmask = 1;
        ipmask = ipmask << mask;
        ipmask--;
        ipmask = ipmask << (32 - mask);
        String[] ips = new String[(int) count];
        int ipsubint = (int) (getIntForIP(ip) & ipmask);
        for (int i = 0; i < count; i++) {
            ips[i] = getStringForIP(ipsubint++);
        }
        return ips;
    }

    public static boolean isSiteLocalAddress(String ip) {
        int address = (int) getIntForIP(ip);
        return (((address >>> 24) & 0xFF) == 10)
                || ((((address >>> 24) & 0xFF) == 172)
                && (((address >>> 16) & 0xF0) == 16))
                || ((((address >>> 24) & 0xFF) == 192)
                && (((address >>> 16) & 0xFF) == 168));
    }

    private static byte[] splitIpTobytes(String ip) {
        byte[] bs = new byte[4];
        String[] ips = ip.split("[.]");
        for (int i = 0; i < 4; i++) {
            int v = Integer.parseInt(ips[i]);
            byte b = (byte) (v > 128 ? v - 256 : v);
            bs[i] = b;
        }
        return bs;
    }

    public static boolean isReachAble(String ip) {
        return isReachAble(ip, 5000);
    }

    public static boolean isReachAble(String ip, int timeout) {
        try {
            InetAddress addr = InetAddress.getByAddress(splitIpTobytes(ip));
            System.out.println("ip=" + addr.getHostAddress());
            return addr.isReachable(timeout);
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
            Logger.getLogger(IpAddress.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }
}
